/*
 * Tencent is pleased to support the open source community by making TBase available.  
 * 
 * Copyright (C) 2019 Tencent.  All rights reserved.
 * 
 * TBase is licensed under the BSD 3-Clause License, except for the third-party component listed below. 
 * 
 * A copy of the BSD 3-Clause License is included in this file.
 * 
 * Other dependencies and licenses:
 * 
 * Open Source Software Licensed Under the PostgreSQL License: 
 * --------------------------------------------------------------------
 * 1. Postgres-XL XL9_5_STABLE
 * Portions Copyright (c) 2015-2016, 2ndQuadrant Ltd
 * Portions Copyright (c) 2012-2015, TransLattice, Inc.
 * Portions Copyright (c) 2010-2017, Postgres-XC Development Group
 * Portions Copyright (c) 1996-2015, The PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, The Regents of the University of California
 * 
 * Terms of the PostgreSQL License: 
 * --------------------------------------------------------------------
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without a written agreement
 * is hereby granted, provided that the above copyright notice and this
 * paragraph and the following two paragraphs appear in all copies.
 * 
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
 * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
 * DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * 
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 * 
 * 
 * Terms of the BSD 3-Clause License:
 * --------------------------------------------------------------------
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation 
 * and/or other materials provided with the distribution.
 * 
 * 3. Neither the name of Tencent nor the names of its contributors may be used to endorse or promote products derived from this software without 
 * specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 
 * DAMAGE.
 * 
 */
/*
 * mbuf.c
 *        Memory buffer operations.
 *
 * Copyright (c) 2005 Marko Kreen
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * contrib/pgcrypto/mbuf.c
 */

#include "postgres.h"

#include "contrib/pgcrypto/px.h"
#include "contrib/pgcrypto/mbuf.h"

#define STEP  (16*1024)

struct MBuf
{
    uint8       *data;
    uint8       *data_end;
    uint8       *read_pos;
    uint8       *buf_end;
    bool        no_write;
    bool        own_data;
};

int
mbuf_avail(MBuf *mbuf)
{
    return mbuf->data_end - mbuf->read_pos;
}

int
mbuf_size(MBuf *mbuf)
{
    return mbuf->data_end - mbuf->data;
}

int
mbuf_tell(MBuf *mbuf)
{
    return mbuf->read_pos - mbuf->data;
}

int
mbuf_free(MBuf *mbuf)
{
    if (mbuf->own_data)
    {
        crypt_memset(mbuf->data, 0, mbuf->buf_end - mbuf->data);
        crypt_free(mbuf->data);
    }
    crypt_free(mbuf);
    return 0;
}

static void
prepare_room(MBuf *mbuf, int block_len)
{
    uint8       *newbuf;
    unsigned    newlen;

    if (mbuf->data_end + block_len <= mbuf->buf_end)
        return;

    newlen = (mbuf->buf_end - mbuf->data)
        + ((block_len + STEP + STEP - 1) & -STEP);

    newbuf = crypt_realloc(mbuf->data, newlen);

    mbuf->buf_end = newbuf + newlen;
    mbuf->data_end = newbuf + (mbuf->data_end - mbuf->data);
    mbuf->read_pos = newbuf + (mbuf->read_pos - mbuf->data);
    mbuf->data = newbuf;

    return;
}

int
mbuf_append(MBuf *dst, const uint8 *buf, int len)
{
    if (dst->no_write)
    {
        px_debug("mbuf_append: no_write");
        return PXE_BUG;
    }

    prepare_room(dst, len);

    memcpy(dst->data_end, buf, len);
    dst->data_end += len;

    return 0;
}

MBuf *
mbuf_create(int len)
{
    MBuf       *mbuf;

    if (!len)
        len = 8192;

    mbuf = crypt_alloc(sizeof *mbuf);
    mbuf->data = crypt_alloc(len);
    mbuf->buf_end = mbuf->data + len;
    mbuf->data_end = mbuf->data;
    mbuf->read_pos = mbuf->data;

    mbuf->no_write = false;
    mbuf->own_data = true;

    return mbuf;
}

MBuf *
mbuf_create_from_data(uint8 *data, int len)
{
    MBuf       *mbuf;

    mbuf = crypt_alloc(sizeof *mbuf);
    mbuf->data = (uint8 *) data;
    mbuf->buf_end = mbuf->data + len;
    mbuf->data_end = mbuf->data + len;
    mbuf->read_pos = mbuf->data;

    mbuf->no_write = true;
    mbuf->own_data = false;

    return mbuf;
}


int
mbuf_grab(MBuf *mbuf, int len, uint8 **data_p)
{
    if (len > mbuf_avail(mbuf))
        len = mbuf_avail(mbuf);

    mbuf->no_write = true;

    *data_p = mbuf->read_pos;
    mbuf->read_pos += len;
    return len;
}

int
mbuf_rewind(MBuf *mbuf)
{
    mbuf->read_pos = mbuf->data;
    return 0;
}

int
mbuf_steal_data(MBuf *mbuf, uint8 **data_p)
{
    int            len = mbuf_size(mbuf);

    mbuf->no_write = true;
    mbuf->own_data = false;

    *data_p = mbuf->data;

    mbuf->data = mbuf->data_end = mbuf->read_pos = mbuf->buf_end = NULL;

    return len;
}

/*
 * PullFilter
 */

struct PullFilter
{
    PullFilter *src;
    const PullFilterOps *op;
    int            buflen;
    uint8       *buf;
    int            pos;
    void       *priv;
};

int
pullf_create(PullFilter **pf_p, const PullFilterOps *op, void *init_arg, PullFilter *src)
{
    PullFilter *pf;
    void       *priv;
    int            res;

    if (op->init != NULL)
    {
        res = op->init(&priv, init_arg, src);
        if (res < 0)
            return res;
    }
    else
    {
        priv = init_arg;
        res = 0;
    }

    pf = crypt_alloc(sizeof(*pf));
    memset(pf, 0, sizeof(*pf));
    pf->buflen = res;
    pf->op = op;
    pf->priv = priv;
    pf->src = src;
    if (pf->buflen > 0)
    {
        pf->buf = crypt_alloc(pf->buflen);
        pf->pos = 0;
    }
    else
    {
        pf->buf = NULL;
        pf->pos = 0;
    }
    *pf_p = pf;
    return 0;
}

void
pullf_free(PullFilter *pf)
{
    if (pf->op->free)
        pf->op->free(pf->priv);

    if (pf->buf)
    {
        crypt_memset(pf->buf, 0, pf->buflen);
        crypt_free(pf->buf);
    }

    crypt_memset(pf, 0, sizeof(*pf));
    crypt_free(pf);
}

/* may return less data than asked, 0 means eof */
int
pullf_read(PullFilter *pf, int len, uint8 **data_p)
{
    int            res;

    if (pf->op->pull)
    {
        if (pf->buflen && len > pf->buflen)
            len = pf->buflen;
        res = pf->op->pull(pf->priv, pf->src, len, data_p,
                           pf->buf, pf->buflen);
    }
    else
        res = pullf_read(pf->src, len, data_p);
    return res;
}

int
pullf_read_max(PullFilter *pf, int len, uint8 **data_p, uint8 *tmpbuf)
{
    int            res,
                total;
    uint8       *tmp;

    res = pullf_read(pf, len, data_p);
    if (res <= 0 || res == len)
        return res;

    /* read was shorter, use tmpbuf */
    memcpy(tmpbuf, *data_p, res);
    *data_p = tmpbuf;
    len -= res;
    total = res;

    while (len > 0)
    {
        res = pullf_read(pf, len, &tmp);
        if (res < 0)
        {
            /* so the caller must clear only on success */
            crypt_memset(tmpbuf, 0, total);
            return res;
        }
        if (res == 0)
            break;
        memcpy(tmpbuf + total, tmp, res);
        total += res;
        len -= res;
    }
    return total;
}

/*
 * caller wants exactly len bytes and don't bother with references
 */
int
pullf_read_fixed(PullFilter *src, int len, uint8 *dst)
{
    int            res;
    uint8       *p;

    res = pullf_read_max(src, len, &p, dst);
    if (res < 0)
        return res;
    if (res != len)
    {
        px_debug("pullf_read_fixed: need=%d got=%d", len, res);
        return PXE_PGP_CORRUPT_DATA;
    }
    if (p != dst)
        memcpy(dst, p, len);
    return 0;
}

/*
 * read from MBuf
 */
static int
pull_from_mbuf(void *arg, PullFilter *src, int len,
               uint8 **data_p, uint8 *buf, int buflen)
{
    MBuf       *mbuf = arg;

    return mbuf_grab(mbuf, len, data_p);
}

static const struct PullFilterOps mbuf_reader = {
    NULL, pull_from_mbuf, NULL
};

int
pullf_create_mbuf_reader(PullFilter **mp_p, MBuf *src)
{
    return pullf_create(mp_p, &mbuf_reader, src, NULL);
}


/*
 * PushFilter
 */

struct PushFilter
{
    PushFilter *next;
    const PushFilterOps *op;
    int            block_size;
    uint8       *buf;
    int            pos;
    void       *priv;
};

int
pushf_create(PushFilter **mp_p, const PushFilterOps *op, void *init_arg, PushFilter *next)
{
    PushFilter *mp;
    void       *priv;
    int            res;

    if (op->init != NULL)
    {
        res = op->init(next, init_arg, &priv);
        if (res < 0)
            return res;
    }
    else
    {
        priv = init_arg;
        res = 0;
    }

    mp = crypt_alloc(sizeof(*mp));
    memset(mp, 0, sizeof(*mp));
    mp->block_size = res;
    mp->op = op;
    mp->priv = priv;
    mp->next = next;
    if (mp->block_size > 0)
    {
        mp->buf = crypt_alloc(mp->block_size);
        mp->pos = 0;
    }
    else
    {
        mp->buf = NULL;
        mp->pos = 0;
    }
    *mp_p = mp;
    return 0;
}

void
pushf_free(PushFilter *mp)
{
    if (mp->op->free)
        mp->op->free(mp->priv);

    if (mp->buf)
    {
        crypt_memset(mp->buf, 0, mp->block_size);
        crypt_free(mp->buf);
    }

    crypt_memset(mp, 0, sizeof(*mp));
    crypt_free(mp);
}

void
pushf_free_all(PushFilter *mp)
{
    PushFilter *tmp;

    while (mp)
    {
        tmp = mp->next;
        pushf_free(mp);
        mp = tmp;
    }
}

static int
wrap_process(PushFilter *mp, const uint8 *data, int len)
{
    int            res;

    if (mp->op->push != NULL)
        res = mp->op->push(mp->next, mp->priv, data, len);
    else
        res = pushf_write(mp->next, data, len);
    if (res > 0)
        return PXE_BUG;
    return res;
}

/* consumes all data, returns len on success */
int
pushf_write(PushFilter *mp, const uint8 *data, int len)
{
    int            need,
                res;

    /*
     * no buffering
     */
    if (mp->block_size <= 0)
        return wrap_process(mp, data, len);

    /*
     * try to empty buffer
     */
    need = mp->block_size - mp->pos;
    if (need > 0)
    {
        if (len < need)
        {
            memcpy(mp->buf + mp->pos, data, len);
            mp->pos += len;
            return 0;
        }
        memcpy(mp->buf + mp->pos, data, need);
        len -= need;
        data += need;
    }

    /*
     * buffer full, process
     */
    res = wrap_process(mp, mp->buf, mp->block_size);
    if (res < 0)
        return res;
    mp->pos = 0;

    /*
     * now process directly from data
     */
    while (len > 0)
    {
        if (len > mp->block_size)
        {
            res = wrap_process(mp, data, mp->block_size);
            if (res < 0)
                return res;
            data += mp->block_size;
            len -= mp->block_size;
        }
        else
        {
            memcpy(mp->buf, data, len);
            mp->pos += len;
            break;
        }
    }
    return 0;
}

int
pushf_flush(PushFilter *mp)
{
    int            res;

    while (mp)
    {
        if (mp->block_size > 0)
        {
            res = wrap_process(mp, mp->buf, mp->pos);
            if (res < 0)
                return res;
        }

        if (mp->op->flush)
        {
            res = mp->op->flush(mp->next, mp->priv);
            if (res < 0)
                return res;
        }

        mp = mp->next;
    }
    return 0;
}


/*
 * write to MBuf
 */
static int
push_into_mbuf(PushFilter *next, void *arg, const uint8 *data, int len)
{
    int            res = 0;
    MBuf       *mbuf = arg;

    if (len > 0)
        res = mbuf_append(mbuf, data, len);
    return res < 0 ? res : 0;
}

static const struct PushFilterOps mbuf_filter = {
    NULL, push_into_mbuf, NULL, NULL
};

int
pushf_create_mbuf_writer(PushFilter **res, MBuf *dst)
{
    return pushf_create(res, &mbuf_filter, dst, NULL);
}
